import Vue from 'vue';
import axios from 'axios';
import store from '@/store';
import router from '@/router';
import contentDisposition from 'content-disposition';
import { getLocalData } from '@/utils/localStore';
import { getClientAuth } from '@/plugins/clientAuth';

import {
  baseURL,
  contentType,
  tokenName,
  requestTimeout,
  messageDuration,
  successCode, // 请求成功
  // invalidCode, // 请求失败
  noPermissionCode, // 没有权限
} from '@/config/settings';

const isInclude = (array, data) => Array.prototype.includes.call(array, data);

const NoMsgApiList = ['/bnd-uaac/advance/commitAllocateAmount'];
const NoMsgCodes = [999];

const showErrorMsg = (message = '请求异常') => {
  Vue.prototype.$message({
    type: 'error',
    showClose: true,
    duration: messageDuration || 2000,
    message,
  });
};

const handleErrorCode = async (status, code, msg) => {
  showErrorMsg(msg || `请求${status}异常: {code: ${code}}`);
  if (noPermissionCode.includes(code)) {
    await store.dispatch('user/removeAccessToken');
    if (getLocalData('client')) {
      // 重新获取客户端授权，参数约定 type = 2
      const clientAuthInfo = await getClientAuth(2);
      await store.dispatch('user/setClientAuth', clientAuthInfo); // 设置由客户端携带的token和用户信息
      location.reload();
    } else {
      await router.push({ path: '/401' });
    }
  }
};

const useInterceptors = instance => {
  instance.interceptors.request.use(
    config => {
      if (store.getters['user/accessToken']) {
        config.headers[tokenName] = store.getters['user/accessToken'];
      }
      return config;
    },
    error => {
      return Promise.reject(error);
    }
  );

  instance.interceptors.response.use(
    async response => {
      const { status, data, config, headers } = response;
      if (
        data.status &&
        !isInclude(successCode, data.status) &&
        !NoMsgApiList.includes(config.url) &&
        !NoMsgCodes.includes(data.status)
      ) {
        await handleErrorCode(status, data.status, data.message);
      }
      if (isInclude(successCode, status)) {
        const type = headers['content-type'];
        if (type.includes('application/json')) {
          return data;
        }
        // 文件下载
        if (
          headers['content-disposition'] &&
          (type.includes('application/octet-stream') ||
            type.includes('application/msexcel') ||
            type.includes('application/vnd.ms-excel'))
        ) {
          let filename = '';
          try {
            const disposition = contentDisposition.parse(headers['content-disposition']);
            if (disposition && disposition.parameters) {
              filename = disposition.parameters.filename;
            }
          } catch (error) {}

          return { data, filename };
        }
        return response;
      }

      return Promise.reject(
        '请求被异常拦截：' +
          JSON.stringify({ url: config.url, status: data.status || status, msg: data.message })
      );
    },
    error => {
      const { response, message } = error;
      if (response && response.data) {
        const { status, data } = response;
        handleErrorCode(status, data.status || status, data.message || message);
        return Promise.reject(error);
      }
      const convertMsg = message => {
        if (message.includes('Network Error')) {
          return '后端接口连接异常';
        }
        if (message.includes('timeout')) {
          return '后端接口请求超时';
        }
        if (message.includes('Request failed with status code')) {
          const code = message.substr(message.length - 3);
          return '后端接口' + code + '异常';
        }
        return '后端接口未知异常';
      };
      showErrorMsg(convertMsg(message));
      return Promise.reject(error);
    }
  );
};

export const request = options => {
  const instance = axios.create({
    baseURL,
    timeout: requestTimeout,
    headers: {
      'Content-Type': contentType,
    },
  });
  useInterceptors(instance);
  return instance(options);
};

// 扩充request的请求方式
function extendRequest(request) {
  const otherMethods = ['delete', 'get', 'head', 'options', 'post', 'put', 'patch'].reduce(
    (all, name) => {
      const fn = (url, data, config) =>
        request({
          url,
          method: name.toUpperCase(),
          [name.startsWith('p') ? 'data' : 'params']: data,
          ...config,
        });
      return { ...all, [name]: fn };
    },
    {}
  );
  const upload = (url, data, config) =>
    request({
      url,
      method: 'POST',
      data,
      headers: {
        'Content-Type': 'multipart/form-data',
      },
      ...config,
    });
  return {
    request,
    ...otherMethods,
    upload,
  };
}

export default extendRequest(request);
